using System.Collections.Generic;
using System.Linq;
using System.Modules.Contracts.DTO;
using System.Modules.ExceptionHanlding;
using System.Modules.Extensions;
using System.Threading;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Utilities;

namespace System.Modules.SharePoint.Extensions
{
    /// <summary>
    /// SPWeb Extensions
    /// </summary>
    public static class SPWebExtensions
    {
        #region Activate Feature
        /// <summary>
        /// Use this method to Activate SP Web Feature
        /// </summary>
        /// <param name="web"></param>
        /// <param name="featureId"> </param>
        public static void ActivateFeature(this SPWeb web, Guid featureId)
        {
            try
            {
                SPFeature activeFeature = web.Features.FirstOrDefault(feature => feature.Definition.Id.Equals(featureId));
                if (activeFeature == null)
                {
                    web.Features.Add(featureId);
                }
            }
            catch (Exception exception)
            {
                exception.Handle();
            }
        }
        #endregion

        #region Deactivate Feature
        /// <summary>
        /// Use this method to Deactivates SP Web Feature
        /// </summary>
        /// <param name="web"></param>
        /// <param name="featureId"></param>
        public static void DeactivateFeature(this SPWeb web, Guid featureId)
        {
            try
            {
                SPFeature activeFeature = web.Features.FirstOrDefault(feature => feature.Definition.Id.Equals(featureId));
                if (activeFeature != null)
                {
                    web.Features.Remove(featureId);
                }
            }
            catch (Exception exception)
            {
                exception.Handle();
            }
        }
        #endregion

        #region Ensure Web
        /// <summary>
        /// Use this method to ensure getting a SP Web.
        /// If a web does not exists, a new SP Web will be created.
        /// </summary>
        /// <param name="parentWeb">SPWeb</param>
        /// <param name="webName">string</param>
        /// <returns></returns>
        public static SPWeb EnsureWeb(this SPWeb parentWeb, string webName)
        {
            return parentWeb.EnsureWeb(webName, webName, string.Empty);
        }

        /// <summary>
        /// Use this method to ensure getting a SP Web.
        /// If a web does not exists, a new SP Web will be created.
        /// </summary>
        /// <param name="parentWeb">SPWeb</param>
        /// <param name="webName">string</param>
        /// <param name="title">string</param>
        /// <returns></returns>
        public static SPWeb EnsureWeb(this SPWeb parentWeb, string webName, string title)
        {
            return parentWeb.EnsureWeb(webName, title, string.Empty);
        }

        /// <summary>
        /// Use this method to ensure getting a SP Web.
        /// If a web does not exists, a new SP Web will be created.
        /// </summary>
        /// <param name="parentWeb">SPWeb</param>
        /// <param name="name">string</param>
        /// <param name="title">string</param>
        /// <param name="description">string</param>
        /// <returns></returns>
        public static SPWeb EnsureWeb(this SPWeb parentWeb, string name, string title, string description)
        {
            SPWeb web = null;
            if (!name.IsNullOrEmpty())
            {
                string webName = name.RemoveIllegalFileNameCharachters(true);
                try
                {
                    web = parentWeb.Webs[webName] ?? parentWeb.Webs[name];
                }
                catch
                {
                    web = null;
                }
                finally
                {
                    if (web == null)
                    {
                        try
                        {
                            web = parentWeb.Webs.Add(webName);
                            var values = new Dictionary<string, object> {{typeof (SPWeb).Name, web}, {"Title", title.IsNullOrEmpty() ? name : title}, {"Description", description}};
                            WaitSiteProvisiong(values);
                        }
                        catch
                        {
                            web = null;
                        }
                    }
                }
            }
            return web;
        }
        #endregion

        #region Ensure New Web
        /// <summary>
        /// Use this method to ensure getting a SP Web.
        /// If a web already exists, it will be deleted and a new SP Web will be created.
        /// </summary>
        /// <param name="parentWeb">SPWeb</param>
        /// <param name="webName">string</param>
        /// <returns></returns>
        public static SPWeb EnsureNewWeb(this SPWeb parentWeb, string webName)
        {
            return parentWeb.EnsureNewWeb(webName, webName, string.Empty);
        }

        /// <summary>
        /// Use this method to ensure getting a SP Web.
        /// If a web already exists, it will be deleted and a new SP Web will be created.
        /// </summary>
        /// <param name="parentWeb">SPWeb</param>
        /// <param name="webName">string</param>
        /// <param name="title">string</param>
        /// <returns></returns>
        public static SPWeb EnsureNewWeb(this SPWeb parentWeb, string webName, string title)
        {
            return parentWeb.EnsureNewWeb(webName, title, string.Empty);
        }

        /// <summary>
        /// Use this method to ensure getting a SP Web.
        /// If a web already exists, it will be deleted and a new SP Web will be created.
        /// </summary>
        /// <param name="parentWeb">SPWeb</param>
        /// <param name="name">string</param>
        /// <param name="title">string</param>
        /// <param name="description">string</param>
        /// <returns></returns>
        public static SPWeb EnsureNewWeb(this SPWeb parentWeb, string name, string title, string description)
        {
            SPWeb web = null;
            if (!name.IsNullOrEmpty())
            {
                string webName = name.RemoveIllegalFileNameCharachters(true);
                try
                {
                    web = parentWeb.Webs[webName] ?? parentWeb.Webs[name];
                }
                catch
                {
                    web = null;
                }
                finally
                {
                    if (web != null)
                    {
                        if (web.Exists)
                        {
                            try
                            {
                                web.DeleteRecursive();
                            }
                            catch (Exception exception)
                            {
                                exception.Handle();
                            }
                            finally
                            {
                                web.Dispose();
                            }
                        }
                    }
                    try
                    {
                        web = parentWeb.Webs.Add(webName);
                        var values = new Dictionary<string, object> {{typeof (SPWeb).Name, web}, {"Title", title.IsNullOrEmpty() ? name : title}, {"Description", description}};
                        WaitSiteProvisiong(values);
                        web.Dispose();
                    }
                    finally
                    {
                        try
                        {
                            web = parentWeb.Webs[webName] ?? parentWeb.Webs[name];
                        }
                        catch
                        {
                            web = null;
                        }
                    }
                }
            }
            return web;
        }
        #endregion

        #region Ensure List
        /// <summary>
        /// Use this Method to Ensure getting a List by Name
        /// If a list does not exists, it will be created.
        /// </summary>
        /// <param name="web">SPWeb</param>
        /// <param name="listName"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static SPList EnsureList(this SPWeb web, string listName, SPListTemplateType type)
        {
            return web.EnsureList(listName, listName, type);
        }

        /// <summary>
        /// Use this Method to Ensure getting a List by Name
        /// If a list does not exists, it will be created.
        /// </summary>
        /// <param name="web"></param>
        /// <param name="listName"></param>
        /// <param name="listDescription"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static SPList EnsureList(this SPWeb web, string listName, string listDescription, SPListTemplateType type)
        {
            SPList list = null;
            if (!listName.IsNullOrEmpty())
            {
                list = web.Lists.TryGetList(listName) ?? web.GetListFromName(listName);
                if (list == null)
                {
                    Guid listId = web.Lists.Add(listName, listDescription, type);
                    list = web.Lists[listId];
                }
            }
            return list;
        }
        #endregion

        #region Ensure New List
        /// <summary>
        /// Use this method to Ensure a new List on the selected web.
        /// This Method Deletes any existing List with the same name, and creates a new List 
        /// </summary>
        /// <param name="web"></param>
        /// <param name="listName"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static SPList EnsureNewList(this SPWeb web, string listName, SPListTemplateType type)
        {
            return web.EnsureNewList(listName, listName, type);
        }

        /// <summary>
        /// Use this method to Ensure a new List on the selected web.
        /// This Method Deletes any existing List with the same name, and creates a new List 
        /// </summary>
        /// <param name="web"></param>
        /// <param name="listName"></param>
        /// <param name="listDescription"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static SPList EnsureNewList(this SPWeb web, string listName, string listDescription, SPListTemplateType type)
        {
            SPList list = null;
            if (!listName.IsNullOrEmpty())
            {
                list = web.Lists.TryGetList(listName);
                if (!listName.Equals("Documents", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (list != null)
                    {
                        list.Delete();
                    }
                    Guid listId = web.Lists.Add(listName, listDescription, type);
                    list = web.Lists[listId];
                }
            }
            return list;
        }
        #endregion

        #region GetListByOriginalName
        /// <summary>
        /// Returns an SPList object from its original name (RootFolder).
        /// The RootFolder Name is never changed even if the list is renamed
        /// </summary>
        /// <param name="web">The SPWeb object that contains the list</param>
        /// <param name="listOriginalName">The RootFolder name of the list.</param>
        /// <returns>The list if it was found. Null otherwise</returns>
        public static SPList GetListByOriginalName(this SPWeb web, string listOriginalName)
        {
            return web.Lists.Cast<SPList>().FirstOrDefault(item => Equals(string.Compare(item.RootFolder.Name, listOriginalName, StringComparison.OrdinalIgnoreCase), 0));
        }
        #endregion

        #region GetListFromName
        /// <summary>
        /// Get List From Name
        /// </summary>
        /// <param name="web"></param>
        /// <param name="listName"></param>
        /// <returns></returns>
        public static SPList GetListFromName(this SPWeb web, string listName)
        {
            if (!listName.IsNullOrEmpty())
            {
                SPList list;
                try
                {
                    list = web.GetListFromUrl(string.Format("Lists/{0}/AllItems.aspx", listName));
                }
                catch (Exception)
                {
                    list = null;
                }
                return list;
            }
            return null;
        }
        #endregion

        #region DeleteRecursive
        /// <summary>
        /// Use this method to Delete a Web and All children Sub Webs
        /// </summary>
        /// <param name="web"></param>
        public static void DeleteRecursive(this SPWeb web)
        {
            if (web.Webs.Count > 0)
            {
                foreach (SPWeb subWeb in web.Webs)
                {
                    subWeb.DeleteRecursive();
                }
            }
            else
            {
                web.Delete();
                web.WaitSiteDeletion();
                web.Dispose();
            }
        }
        #endregion

        #region DeleteSubWebs
        /// <summary>
        /// Deletes all Sub Webs Recursively
        /// </summary>
        /// <param name="web"></param>
        public static void DeleteSubWebs(this SPWeb web)
        {
            if (web.Webs.Count > 0)
            {
                foreach (SPWeb subWeb in web.Webs)
                {
                    subWeb.DeleteRecursive();
                }
            }
        }
        #endregion

        #region FixPageLayoutFieldUrl
        /// <summary>
        /// Use this method to fix Page Layout Field Url Value after Site Backup / Restore
        /// </summary>
        /// <param name="web"></param>
        /// <param name="isRecursive"></param>
        public static void FixPageLayoutFieldUrl(this SPWeb web, bool isRecursive)
        {
            if (web != null)
            {
                if (PublishingWeb.IsPublishingWeb(web))
                {
                    PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
                    publishingWeb.FixPageLayoutFieldUrl();
                    if (isRecursive && web.Webs.Count > 0)
                    {
                        foreach (SPWeb spWeb in web.Webs)
                        {
                            spWeb.FixPageLayoutFieldUrl(true);
                        }
                    }
                }
            }
        }
        #endregion

        #region SendEmail
        /// <summary>
        /// Use this method to sends Email from SharePoint.
        /// </summary>
        /// <param name="web"></param>
        /// <param name="email"></param>
        public static void SendEmail(this SPWeb web, EmailActivity email)
        {
            if (web != null)
            {
                SPUtility.SendEmail(web, email.Header, email.Body);
            }
        }
        #endregion

        #region FixLookupField
        /// <summary>
        /// Use this method to restructure Field XML by binding list Id.
        /// </summary>
        /// <param name="web"></param>
        /// <param name="lookUpStaticName"></param>
        /// <param name="listName"></param>
        public static void FixLookupField(this SPWeb web, string lookUpStaticName, string listName)
        {
            if (web != null)
            {
                var field = web.Fields.TryGetFieldByStaticName(lookUpStaticName) as SPFieldLookup;
                SPList list = web.Lists.TryGetList(listName);
                if (field != null && list != null)
                {
                    field.SetLookupList(list);
                }
            }
        }
        #endregion

        #region PopulateChoiceField
        /// <summary>
        ///  Use this method to populate choice field from a SP list.
        /// </summary>
        /// <param name="web"></param>
        /// <param name="pickListStaticName"></param>
        /// <param name="listName"></param>
        public static void PopulateChoiceField(this SPWeb web, string pickListStaticName, string listName)
        {
            if (web != null)
            {
                var field = web.Fields.TryGetFieldByStaticName(pickListStaticName) as SPFieldMultiChoice;
                SPList list = web.Lists.TryGetList(listName);
                if (field != null && list != null)
                {
                    field.Populate(list);
                }
            }
        }

        /// <summary>
        ///   Use this method to populate choice field from a list of values.
        /// </summary>
        /// <param name="web"></param>
        /// <param name="pickListStaticName"></param>
        /// <param name="values"></param>
        public static void PopulateChoiceField(this SPWeb web, string pickListStaticName, List<string> values)
        {
            if (web != null)
            {
                var field = web.Fields.TryGetFieldByStaticName(pickListStaticName) as SPFieldMultiChoice;
                if (field != null)
                {
                    field.Populate(values);
                }
            }
        }
        #endregion

        #region Ensure User
        /// <summary>
        /// Try Get User
        /// </summary>
        /// <param name="web"></param>
        /// <param name="login"></param>
        /// <returns></returns>
        public static SPUser TryGetUser(this SPWeb web, string login)
        {
            SPUser user = null;
            if (web != null && !String.IsNullOrEmpty(login))
            {
                try
                {
                    user = web.EnsureUser(login);
                }
                catch (Exception exception)
                {
                    exception.Handle();
                }
            }
            return user;
        }

        /// <summary>
        /// Try Get User
        /// </summary>
        /// <param name="web"></param>
        /// <param name="login"></param>
        /// <param name="exception"></param>
        /// <returns></returns>
        public static SPUser TryGetUser(this SPWeb web, string login, out Exception exception)
        {
            SPUser user = null;
            exception = null;
            if (web != null && !String.IsNullOrEmpty(login))
            {
                try
                {
                    user = web.EnsureUser(login);
                }
                catch (Exception e)
                {
                    exception = e;
                }
            }
            return user;
        }
        #endregion

        #region Private Methods
        private static void WaitSiteProvisiong(object state)
        {
            WaitSiteProvisiong(state, 1000);
        }

        private static void WaitSiteProvisiong(object state, int millisecondsTimeOut)
        {
            try
            {
                var value = state as Dictionary<string, object>;
                if (value != null)
                {
                    var spWeb = value[typeof (SPWeb).Name] as SPWeb;
                    string title = value["Title"].ToString();
                    string description = value["Description"].ToString();
                    bool success = false;
                    do
                    {
                        Thread.Sleep(millisecondsTimeOut);
                        if (spWeb != null)
                        {
                            using (var site = new SPSite(spWeb.Site.ID))
                            {
                                using (SPWeb web = site.OpenWeb(spWeb.ID))
                                {
                                    success = web.Provisioned;
                                    if (success)
                                    {
                                        web.Title = title;
                                        web.Description = description;
                                        web.Update();
                                    }
                                }
                            }
                        }
                    }
                    while (!success);
                }
            }
            catch (Exception ex)
            {
                ex.Handle();
            }
        }

        private static void WaitSiteDeletion(this SPWeb spWeb)
        {
            WaitSiteDeletion(spWeb, 1000);
        }

        private static void WaitSiteDeletion(this SPWeb spWeb, int millisecondsTimeOut)
        {
            try
            {
                if (spWeb != null)
                {
                    bool success;
                    do
                    {
                        Thread.Sleep(millisecondsTimeOut);
                        using (var site = new SPSite(spWeb.Site.ID))
                        {
                            using (SPWeb web = site.OpenWeb(spWeb.ID))
                            {
                                success = web == null;
                            }
                        }
                    }
                    while (!success);
                }
            }
            catch (Exception ex)
            {
                ex.Handle();
            }
        }
        #endregion
    }
}